-module(creatures).
-export([fence/2, grass/3, rabbit/3, fox/3]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Pasture Project - Advanced Functional Programming
%% Uppsala University - HT 11
%%
%% Module: creatures.erl
%%
%% Author: Daniel Strebel, daniel.strebel@gmail.com
%% Author: Eduardo Hernandez, eduhrr@gmail.com
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Fence
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fence(X, Y) ->
    receive
	{start, Gameboard_PID} ->
	    send_do_nothing(Gameboard_PID),
	    fence(X,Y)
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Grass
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
grass(X, Y, Procreation_timer_max) ->
    grass_loop({X, Y},Procreation_timer_max, Procreation_timer_max).

%% Grass cell that is ready to procreate
grass_loop(Position,Procreation_timer_max, 0) -> 
    receive
	{die, _} ->
	    urks;
	{start, Gameboard_PID} ->
	    %%get the neighbours
	    Gameboard_PID ! {request_neighbors, Position, self()},
	    receive
		{die, _} ->
		    urks;
		{neighbors, Neighbor_List} ->
		    decide_grass(Neighbor_List, Gameboard_PID, Position, 
				 Procreation_timer_max)
	    end
    end;
grass_loop(Position,Procreation_timer_max,Procreation_timer) ->
    receive
	{die, _} ->
	    urks;
	{start, Gameboard_PID} ->
	    send_do_nothing(Gameboard_PID),
	    grass_loop(Position,Procreation_timer_max,Procreation_timer-1)
    end.

decide_grass(Neighbor_List, Gameboard_PID, Position, Procreation_timer_max) ->
    Free_Cells = [{X_cell, Y_cell} || {_, Type, X_cell, Y_cell}<-Neighbor_List, Type==none],
    case Free_Cells of
	[] -> %no free surrounding cells
	    send_do_nothing(Gameboard_PID),
	    grass_loop(Position,Procreation_timer_max,0);
	_ ->
	    {Child_X, Child_Y} = lists:nth(random:uniform(length(Free_Cells)), Free_Cells),
	    Child_Pid = spawn(creatures, grass, [Child_X, Child_Y, 5]),
	    Gameboard_PID ! {procreate, Child_Pid, grass, Position, {Child_X, Child_Y}, self() },
	    receive
		{ok, Gameboard_PID} ->
		    grass_loop(Position, Procreation_timer_max, Procreation_timer_max);
		{conflict, New_Neighbor_List}->
		    decide_grass(New_Neighbor_List, Gameboard_PID, Position, Procreation_timer_max); 
		{die, _} ->
		    urks
	    end
    end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Rabbit
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Rabbit initialization
rabbit(X, Y, {Eat_timer_max, Procreation_timer_max, Starvation_timer_max, Move_timer_max}) ->
    animal_loop({X, Y}, {Eat_timer_max, Procreation_timer_max, Starvation_timer_max, Move_timer_max}, 
		{Eat_timer_max, Procreation_timer_max, Starvation_timer_max, Move_timer_max}, {rabbit, grass}).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Fox
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Fox initialization
fox(X, Y, {Eat_timer_max, Procreation_timer_max, Starvation_timer_max, Move_timer_max}) ->
    animal_loop({X, Y}, {Eat_timer_max, Procreation_timer_max, Starvation_timer_max, Move_timer_max},
		{Eat_timer_max, Procreation_timer_max, Starvation_timer_max, Move_timer_max}, {fox, rabbit}).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% General Animal Code
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Starving animal
animal_loop({X, Y},  {_, _, 0, _}, _, _) ->
    receive
	{die, _} ->
	    urks;
	{start, Gameboard_PID} ->
	    Gameboard_PID ! {starve, X, Y, self()},
	    receive
		{ok, _} ->
		    urks
	    end
    end;
%% Decision loop for animals
animal_loop(Position, States, Default_States, Types) ->
    receive
	{die, _} ->
	    urks;
	{start, Gameboard_PID} ->
	    Gameboard_PID ! {request_neighbors, Position, self()},
	    receive
		{die, _} ->
		    urks;
		{neighbors, Neighbor_List} ->
		    decide_animal(Neighbor_List, Gameboard_PID, Position, States, Default_States, Types)
	    end
    end.

decide_animal(Neighbor_List, Gameboard_PID, Position, States, Default_States, Types) ->
    {Own_Type, Prey_Type} = Types,
    {Eat_timer, Procreation_timer, Starvation_timer, Move_timer} = States,
    Prey_Cells = [{X_cell, Y_cell} || {_, Type, X_cell, Y_cell}<-Neighbor_List, Type==Prey_Type],
    Free_Cells = [{X_cell, Y_cell} || {_, Type, X_cell, Y_cell}<-Neighbor_List, Type==none],
    case {Free_Cells, Prey_Cells, Move_timer, Eat_timer, Procreation_timer} of
	{[_|_], _, 0, _, _} -> % move timer is zero and there is at least one neighboring empty cell
	    {_, _, _, Move_timer_max} = Default_States,
	    New_Position = lists:nth(random:uniform(length(Free_Cells)), Free_Cells),
	    Gameboard_PID ! {move, Position, New_Position, self()},
	    receive
		{ok, Gameboard_PID} ->
		    animal_loop(New_Position, {max(Eat_timer-1, 0), max(Procreation_timer-1, 0), Starvation_timer-1, Move_timer_max}, Default_States, Types);
		{conflict, New_Neighbor_List} ->
		    decide_animal(New_Neighbor_List, Gameboard_PID, Position, States, Default_States, Types); %reevaluate with current situation 
		{die, _} ->
		    urks
	    end;
	{_, [_|_], _, 0, 0} -> % eat timer and procreation timer are zero and there is at least one neighboring grass cell
	    {Own_X, Own_Y} = Position,
	    {Prey_X, Prey_Y} = lists:nth(random:uniform(length(Prey_Cells)), Prey_Cells),
	    Child_Pid = spawn(creatures, Own_Type, [Own_X, Own_Y, Default_States]),
	    Gameboard_PID!{eat, Position, {Prey_X, Prey_Y}, Prey_Type, Child_Pid, self()},
	    receive
		{ok, Gameboard_PID} -> %procreation_timer will be equal to eat timer after it has expired for the first time. 
		    {Eat_timer_max, _, Starvation_timer_max, _} = Default_States,
		    animal_loop({Prey_X, Prey_Y}, {Eat_timer_max, Eat_timer_max, Starvation_timer_max, Move_timer-1}, Default_States,  Types);
		{conflict, New_Neighbor_List} ->
		    Child_Pid ! {die, self()}, %destroy the created child process
		    decide_animal(New_Neighbor_List, Gameboard_PID, Position, States, Default_States, Types); %reevaluate with current situation
		{die, _} ->
		    urks
	    end;
	{_, _, _, _, _} -> % no possible actions
	    send_do_nothing(Gameboard_PID),
	    animal_loop(Position, {max(Eat_timer-1, 0), max(Procreation_timer-1,0), max(Starvation_timer-1,0), max(Move_timer-1,0)}, Default_States, Types)
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% General Utility
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

send_do_nothing(Gameboard_PID) ->
    Gameboard_PID ! {do_nothing, self()},
    receive
    {ok, Gameboard_PID} ->
	    ok;
    {die, _} ->
            self() ! {die, self()}    
    end.
